package com.ruoyi.security.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.security.convert.SecurityTradingCalendarConvert;
import com.ruoyi.security.domain.SecurityTradingCalendar;
import com.ruoyi.security.mapper.SecurityTradingCalendarMapper;
import com.ruoyi.security.service.ISecurityTradingCalendarService;
import com.ruoyi.security.service.craw.ICrawSecurityTradingCalendarService;
import com.ruoyi.security.vo.SecurityTradingCalendarResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 证券交易日历Service业务层处理
 * 
 * @author yeoman
 * @date 2025-01-15
 */
@Slf4j
@Service
public class SecurityTradingCalendarServiceImpl implements ISecurityTradingCalendarService {
    
    @Resource
    private SecurityTradingCalendarMapper securityTradingCalendarMapper;
    
    @Resource
    private ICrawSecurityTradingCalendarService crawSecurityTradingCalendarService;

    /**
     * 同步交易日历数据到数据库
     * 
     * @param year 年份
     * @return 同步结果
     */
    @Override
    public String sync(Integer year) {
        try {
            log.info("开始同步{}年交易日历数据", year);
            
            int successCount = 0;
            int failCount = 0;
            
            // 循环调用每个月的同步方法
            for (int month = 1; month <= 12; month++) {
                try {
                    String monthResult = sync(year, month);
                    // 解析月份同步结果中的成功和失败条数
                    if (monthResult.contains("成功：") && monthResult.contains("失败：")) {
                        String[] parts = monthResult.split("成功：")[1].split("条");
                        if (parts.length > 0) {
                            try {
                                successCount += Integer.parseInt(parts[0]);
                            } catch (NumberFormatException e) {
                                log.warn("解析成功条数失败: {}", parts[0]);
                            }
                        }
                        String[] failParts = monthResult.split("失败：")[1].split("条");
                        if (failParts.length > 0) {
                            try {
                                failCount += Integer.parseInt(failParts[0]);
                            } catch (NumberFormatException e) {
                                log.warn("解析失败条数失败: {}", failParts[0]);
                            }
                        }
                    }
                } catch (Exception e) {
                    log.error("同步{}年{}月交易日历数据失败: {}", year, month, e.getMessage());
                    failCount++;
                }
            }
            
            String result = String.format("同步完成，成功：%d条，失败：%d条", successCount, failCount);
            log.info(result);
            return result;
            
        } catch (Exception e) {
            String errorMsg = "同步交易日历数据失败: " + e.getMessage();
            log.error(errorMsg, e);
            return errorMsg;
        }
    }
    
    /**
     * 同步指定年月的交易日历数据到数据库
     * 
     * @param year 年份
     * @param month 月份（1-12）
     * @return 同步结果
     */
    @Override
    public String sync(Integer year, Integer month) {
        try {
            log.info("开始同步{}年{}月交易日历数据", year, month);
            
            // 爬取指定年月数据
            SecurityTradingCalendarResponse response = crawSecurityTradingCalendarService.craw(year, month);
            if (response == null || response.getData() == null || response.getData().isEmpty()) {
                return "爬取数据为空，同步失败";
            }
            
            int successCount = 0;
            int failCount = 0;
            
                // 转换并保存到数据库
                for (SecurityTradingCalendarResponse.Data data : response.getData()) {
                    try {
                        SecurityTradingCalendar calendar = SecurityTradingCalendarConvert.INSTANCE.toSecurityTradingCalendar(data);
                        
                        // 按日期查询是否已存在
                        SecurityTradingCalendar existingCalendar = securityTradingCalendarMapper.selectOne(new LambdaQueryWrapper<SecurityTradingCalendar>()
                                .eq(SecurityTradingCalendar::getDate, calendar.getDate()));
                        if (existingCalendar != null) {
                            // 存在则更新
                            calendar.setId(existingCalendar.getId());
                            securityTradingCalendarMapper.updateById(calendar);
                            log.debug("更新交易日历数据: {}", calendar.getDate());
                        } else {
                            // 不存在则插入
                            securityTradingCalendarMapper.insert(calendar);
                            log.debug("插入交易日历数据: {}", calendar.getDate());
                        }
                        successCount++;
                    } catch (Exception e) {
                        log.error("保存交易日历数据失败: {}", e.getMessage());
                        failCount++;
                    }
                }
            
            String result = String.format("同步完成，成功：%d条，失败：%d条", successCount, failCount);
            log.info(result);
            return result;
            
        } catch (Exception e) {
            String errorMsg = "同步交易日历数据失败: " + e.getMessage();
            log.error(errorMsg, e);
            return errorMsg;
        }
    }
    
    /**
     * 查询指定年份的交易日历数据，按月份分组
     * 
     * @param year 年份
     * @return 按月份分组的交易日历数据，key为月份(1-12)，value为该月的交易日历列表
     */
    @Override
    public Map<Integer, List<SecurityTradingCalendar>> selectTradingCalendarByYearGroupByMonth(Integer year) {
        // 直接按年份查询数据
        List<SecurityTradingCalendar> calendars = securityTradingCalendarMapper.selectList(new LambdaQueryWrapper<SecurityTradingCalendar>()
                .apply("YEAR(date) = {0}", year).orderByAsc(SecurityTradingCalendar::getDate));

        // 按月份分组
        Map<Integer, List<SecurityTradingCalendar>> monthGroups = calendars.stream()
            .collect(Collectors.groupingBy(item -> {
                Calendar cal = Calendar.getInstance();
                cal.setTime(item.getDate());
                return cal.get(Calendar.MONTH) + 1; // Calendar.MONTH 从0开始，所以+1
            }));
        
        // 确保所有12个月都有对应的列表（即使为空）
        for (int month = 1; month <= 12; month++) {
            monthGroups.putIfAbsent(month, java.util.Collections.emptyList());
        }
        
        return monthGroups;
    }
    
    /**
     * 查询日期范围内的交易日并校验完整性
     * 
     * @param startDate 开始日期
     * @param endDate 结束日期
     * @return 交易日历列表
     * @throws RuntimeException 如果日期范围内缺少交易日历数据
     */
    @Override
    public List<SecurityTradingCalendar> selectTradingDays(Date startDate, Date endDate) {
        if (startDate == null || endDate == null) {
            throw new RuntimeException("开始日期和结束日期不能为空");
        }
        
        if (startDate.after(endDate)) {
            throw new RuntimeException("开始日期不能晚于结束日期");
        }
        
        // 计算开始日期和结束日期之间的天数（包含开始和结束日期）
        long daysBetween = ((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24)) + 1;
        
        // 查询日期范围内的所有交易日历记录（包括非交易日）
        List<SecurityTradingCalendar> allDaysInRange = securityTradingCalendarMapper.selectList(new LambdaQueryWrapper<SecurityTradingCalendar>()
                .between(SecurityTradingCalendar::getDate, startDate, endDate)
                .orderByAsc(SecurityTradingCalendar::getDate));
        long actualCount = allDaysInRange.size();
        
        // 比较查询记录数是否等于天数
        if (actualCount < daysBetween) {
            String errorMsg = String.format("日期范围[%s, %s]内缺少交易日历数据，期望%d条记录，实际%d条记录",
                    com.ruoyi.security.util.DateUtil.parseDateToStr(startDate),
                    com.ruoyi.security.util.DateUtil.parseDateToStr(endDate),
                    daysBetween, actualCount);
            log.error(errorMsg);
            throw new RuntimeException(errorMsg);
        }
        
        // 查询日期范围内的交易日
        List<SecurityTradingCalendar> tradingDays = securityTradingCalendarMapper.selectList(new LambdaQueryWrapper<SecurityTradingCalendar>()
                .between(SecurityTradingCalendar::getDate, startDate, endDate)
                .eq(SecurityTradingCalendar::getTradingDayFlag, 1)
                .orderByAsc(SecurityTradingCalendar::getDate));

        log.info("日期范围[{}, {}]内交易日历数据校验通过，共{}个交易日",
                com.ruoyi.security.util.DateUtil.parseDateToStr(startDate),
                com.ruoyi.security.util.DateUtil.parseDateToStr(endDate),
                tradingDays.size());

        return tradingDays;
    }
}
